package main

import (
	"encoding/json"
	"io"
	"log"
	"os"
	"strconv"

	starter "github.com/10cella/yomo-rcvr-mqtt-starter"
)

var isDebug = getBool("YOMO_RCVR_JSON_MQTT_DEBUG", false)

func CreateServer(addr string, writer io.Writer) { // nolint
	log.SetPrefix("[yomo-rcvr-json-mqtt]")
	log.Println("conn to broker: ", addr)

	starter.NewBroker(&starter.BrokerConf{
		Addr:   addr,
		Topics: []string{"thermometer", "test"},
	}).Sub(func(topic string, payload []byte) {
		payload = starter.RemoveEnd(payload)
		if isDebug {
			log.Printf("[Sub Client Topic] : %s", topic)
			log.Printf("[Sub Client msg] : %s", payload)
		}
		defer func() {
			if err := recover(); err != nil {
				log.Printf("handle error: %v", err)
			}
		}()

		if hasEnd(payload) {
			log.Printf("payload has end tag, topic: %s, payload: %s", topic, payload)
			return
		}

		var (
			flag bool
			maps []map[string]interface{}
			err  error
		)
		flag, maps = isMapArray(payload)
		if flag {
			payload = mergeArrayByTopic(topic, maps)
			if len(payload) == 0 {
				return
			}

			_, err = writer.Write(starter.Pack(payload))
			if err != nil {
				log.Printf("Write error: %s, payload: %s", err.Error(), payload)
			}
		} else {
			payload = mergeObjectByTopic(topic, payload)
			if len(payload) == 0 {
				return
			}

			_, err = writer.Write(starter.Pack(payload))
			if err != nil {
				log.Printf("Write error: %s, payload: %s", err.Error(), payload)
			}
		}
	})

	select {}
}

func hasEnd(payload []byte) bool {
	for _, b := range payload {
		if b == starter.GetEnd() {
			return true
		}
	}
	return false
}

func isMapArray(payload []byte) (bool, []map[string]interface{}) {
	var (
		ss  []map[string]interface{}
		err error
	)

	err = json.Unmarshal(payload, &ss)
	if err != nil {
		return false, nil
	}
	return true, ss
}

func mergeArrayByTopic(topic string, mapArray []map[string]interface{}) []byte {
	var (
		err    error
		output []byte
	)

	var t = make(map[string]interface{})
	t[topic] = mapArray

	output, err = json.Marshal(t)
	if err != nil {
		log.Printf("mergeArrayByTopic json.Marshal error: %s", err.Error())
		return make([]byte, 0)
	}
	return output
}

func mergeObjectByTopic(topic string, payload []byte) []byte {
	var output []byte

	m := toMap(payload)
	if m == nil {
		return make([]byte, 0)
	}

	var t = make(map[string]interface{})
	t[topic] = m
	output, err := json.Marshal(t)
	if err != nil {
		log.Printf("mergeObjectByTopic json.Marshal error: %s", err.Error())
		return make([]byte, 0)
	}

	return output
}

func toMap(buf []byte) map[string]interface{} {
	var m map[string]interface{}
	if err := json.Unmarshal(buf, &m); err != nil {
		log.Printf("not a json, error: %s", err.Error())
		return nil
	}
	return m
}

func getBool(key string, defaultValue bool) bool {
	value := os.Getenv(key)
	if len(value) != 0 {
		flag, err := strconv.ParseBool(value)
		if err != nil {
			return defaultValue
		}
		return flag
	}
	return defaultValue
}
